﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Media;
using System.Windows.Forms;
using CC.Yacht.Interfaces;

namespace CC.Yacht.Windows.Forms
{
    public partial class ScorecardPanel : UserControl, IScorecard
    {
        #region Constructor
        public ScorecardPanel()
        {
            InitializeComponent();

            _IsInitialized = true;

            _Scorecard.Changed += _Scorecard_Changed;

            _DieLabelOnesText.Tag = ScoreField.Aces;
            _LabelOnesValue.Tag = ScoreField.Aces;
            _LabelChanceText.Tag = ScoreField.Chance;
            _LabelChanceValue.Tag = ScoreField.Chance;
            _DieLabelFivesText.Tag = ScoreField.Fives;
            _LabelFivesValue.Tag = ScoreField.Fives;
            _LabelFourOfAKindText.Tag = ScoreField.FourOfAKind;
            _LabelFourOfAKindValue.Tag = ScoreField.FourOfAKind;
            _DieLabelFoursText.Tag = ScoreField.Fours;
            _LabelFoursValue.Tag = ScoreField.Fours;
            _LabelFullHouseText.Tag = ScoreField.FullHouse;
            _LabelFullHouseValue.Tag = ScoreField.FullHouse;
            _LabelLargeStraightText.Tag = ScoreField.LargeStraight;
            _LabelLargeStraightValue.Tag = ScoreField.LargeStraight;
            _DieLabelSixesText.Tag = ScoreField.Sixes;
            _LabelSixesValue.Tag = ScoreField.Sixes;
            _LabelSmallStraightText.Tag = ScoreField.SmallStraight;
            _LabelSmallStraightValue.Tag = ScoreField.SmallStraight;
            _LabelThreeOfAKindText.Tag = ScoreField.ThreeOfAKind;
            _LabelThreeOfAKindValue.Tag = ScoreField.ThreeOfAKind;
            _DieLabelThreesText.Tag = ScoreField.Threes;
            _LabelThreesValue.Tag = ScoreField.Threes;
            _DieLabelTwosText.Tag = ScoreField.Twos;
            _LabelTwosValue.Tag = ScoreField.Twos;
            _LabelYachtText.Tag = ScoreField.Yacht;
            _LabelYachtValue.Tag = ScoreField.Yacht;

            UpdateDisplay();
        }
        #endregion

        #region Private Fields
        private Dice _Dice;
        private Color _HoverColor = Color.DarkOrange;
        private readonly bool _IsInitialized;
        private Scorecard _Scorecard = new Scorecard();
        #endregion

        #region Public Properties
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Dice Dice
        {
            get { return _Dice; }
        }

        public Color HoverColor
        {
            get { return _HoverColor; }
            set { _HoverColor = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Size MaximumSize
        {
            get { return base.MinimumSize; }
            set
            {
                if (!_IsInitialized)
                {
                    base.MaximumSize = value;
                }
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Size MinimumSize
        {
            get { return base.MinimumSize; }
            set
            {
                if (!_IsInitialized)
                {
                    base.MinimumSize = value;
                }
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Scorecard Scorecard
        {
            get { return _Scorecard; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Size Size
        {
            get { return base.Size; }
            set
            {
                if (!_IsInitialized)
                {
                    base.Size = value;
                }
            }
        }
        #endregion

        #region Public Events
        public event EventHandler<ScoreChangedEventArgs> Changed;
        #endregion

        #region Private Event Handlers
        // ReSharper disable InconsistentNaming
        private void _Scorecard_Changed(object sender, ScoreChangedEventArgs e)
        // ReSharper restore InconsistentNaming
        {
            UpdateDisplay();
            OnChanged(e);
        }

        private void Label_Click(object sender, EventArgs e)
        {
            Control control = sender as Control;
            if (control != null)
            {
                if (_Dice != null && _Scorecard.SetScoreField((ScoreField)control.Tag, _Dice))
                {
                    _Dice = null;
                }
                else
                {
                    SystemSounds.Beep.Play();
                }
            }
        }

        private void Label_MouseEnter(object sender, EventArgs e)
        {
            Control senderControl = sender as Control;

            if (senderControl != null && _Dice != null && _Dice.HasRolled)
            {
                senderControl.BackColor = _HoverColor;

                foreach (Control control in senderControl.Parent.Controls)
                {
                    if (control.Tag != null && (ScoreField)control.Tag == (ScoreField)senderControl.Tag)
                    {
                        control.BackColor = _HoverColor;
                    }
                }
            }
        }

        private void Label_MouseLeave(object sender, EventArgs e)
        {
            Control senderControl = sender as Control;

            if (senderControl != null)
            {
                senderControl.BackColor = BackColor;

                foreach (Control control in senderControl.Parent.Controls)
                {
                    if (control.Tag != null && (ScoreField)control.Tag == (ScoreField)senderControl.Tag)
                    {
                        control.BackColor = BackColor;
                    }
                }
            }
        }
        #endregion

        #region Private Methods
        private void SetToolTip(Control control, string caption)
        {
            SetToolTip(control, caption, false);
        }

        private void SetToolTip(Control control, string caption, bool recurse)
        {
            Queue<Control> controlQueue = new Queue<Control>();
            controlQueue.Enqueue(control);

            while (controlQueue.Count > 0)
            {
                Control currentControl = controlQueue.Dequeue();
                _ToolTip.SetToolTip(currentControl, caption);

                if (recurse)
                {
                    foreach (Control childControl in currentControl.Controls)
                    {
                        controlQueue.Enqueue(childControl);
                    }
                }
            }
        }

        private void UpdateDisplay()
        {
            _LabelChanceValue.Text = (_Scorecard.Chance > -1) ? _Scorecard.Chance.ToString() : "-";
            _LabelFivesValue.Text = (_Scorecard.Fives > -1) ? _Scorecard.Fives.ToString() : "-";
            _LabelFourOfAKindValue.Text = (_Scorecard.FourOfAKind > -1) ? _Scorecard.FourOfAKind.ToString() : "-";
            _LabelFoursValue.Text = (_Scorecard.Fours > -1) ? _Scorecard.Fours.ToString() : "-";
            _LabelFullHouseValue.Text = (_Scorecard.FullHouse > -1) ? _Scorecard.FullHouse.ToString() : "-";
            _LabelLargeStraightValue.Text = (_Scorecard.LargeStraight > -1) ? _Scorecard.LargeStraight.ToString() : "-";
            _LabelOnesValue.Text = (_Scorecard.Aces > -1) ? _Scorecard.Aces.ToString() : "-";
            _LabelSixesValue.Text = (_Scorecard.Sixes > -1) ? _Scorecard.Sixes.ToString() : "-";
            _LabelSmallStraightValue.Text = (_Scorecard.SmallStraight > -1) ? _Scorecard.SmallStraight.ToString() : "-";
            _LabelThreeOfAKindValue.Text = (_Scorecard.ThreeOfAKind > -1) ? _Scorecard.ThreeOfAKind.ToString() : "-";
            _LabelThreesValue.Text = (_Scorecard.Threes > -1) ? _Scorecard.Threes.ToString() : "-";
            _LabelTwosValue.Text = (_Scorecard.Twos > -1) ? _Scorecard.Twos.ToString() : "-";
            _LabelYachtValue.Text = (_Scorecard.Yacht > -1) ? _Scorecard.Yacht.ToString() : "-";
            _LabelYachtBonusValue.Text = (_Scorecard.YachtBonuses > 0) ? (_Scorecard.YachtBonuses * Constants.UpperSectionBonusValue).ToString() : "-";

            _LabelLowerSectionTotalValue.Text = _Scorecard.LowerSectionTotal.ToString();
            _LabelUpperSectionBonusValue.Text = _Scorecard.UpperSectionBonus.ToString();
            _LabelUpperSectionSubtotalValue.Text = _Scorecard.UpperSectionSubtotal.ToString();
            _LabelUpperSectionTotalValue1.Text = _Scorecard.UpperSectionTotal.ToString();
            _LabelUpperSectionTotalValue2.Text = _Scorecard.UpperSectionTotal.ToString();
            _LabelTotalValue.Text = _Scorecard.Total.ToString();
        }

        private void UpdateToolTips()
        {
            if (_Dice == null || !_Dice.HasRolled)
            {
                foreach (Control control in _TableLayoutPanelLowerSection.Controls)
                {
                    if (control.Tag != null)
                    {
                        _ToolTip.SetToolTip(control, string.Empty);
                    }
                }

                foreach (Control control in _TableLayoutPanelUpperSection.Controls)
                {
                    if (control.Tag != null)
                    {
                        _ToolTip.SetToolTip(control, string.Empty);
                    }
                }
            }
            else
            {
                foreach (Control control in _TableLayoutPanelLowerSection.Controls)
                {
                    if (control.Tag != null)
                    {
                        ScoreField scoreField = (ScoreField) control.Tag;
                        SetToolTip(control, IsScoreFieldSet(scoreField) ? string.Empty : _Dice.GetScore(scoreField).ToString(), true);
                    }
                }

                foreach (Control control in _TableLayoutPanelUpperSection.Controls)
                {
                    if (control.Tag != null)
                    {
                        ScoreField scoreField = (ScoreField)control.Tag;
                        SetToolTip(control, IsScoreFieldSet(scoreField) ? string.Empty : _Dice.GetScore(scoreField).ToString(), true);
                    }
                }
            }
        }
        #endregion

        #region Protected Methods
        protected void OnChanged(ScoreChangedEventArgs eventArgs)
        {
            if (Changed != null)
            {
                Changed(this, eventArgs);
            }
        }
        #endregion

        #region Public Methods
        public void ClearDice()
        {
            UpdateDice(null);
        }

        public void UpdateDice(Dice dice)
        {
            _Dice = dice;
            UpdateToolTips();
        }

        public void UpdateScore(Scorecard scorecard)
        {
            if (_Scorecard != null)
            {
                _Scorecard.Changed -= _Scorecard_Changed;
            }

            _Scorecard = scorecard;

            if (_Scorecard != null)
            {
                _Scorecard.Changed += _Scorecard_Changed;
            }

            UpdateDisplay();
        }
        #endregion

        #region IScorecard Members
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Aces
        {
            get { return _Scorecard.Aces; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Twos
        {
            get { return _Scorecard.Twos; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Threes
        {
            get { return _Scorecard.Threes; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Fours
        {
            get { return _Scorecard.Fours; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Fives
        {
            get { return _Scorecard.Fives; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Sixes
        {
            get { return _Scorecard.Sixes; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int UpperSectionBonus
        {
            get { return _Scorecard.UpperSectionBonus; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int UpperSectionSubtotal
        {
            get { return _Scorecard.UpperSectionSubtotal; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int UpperSectionTotal
        {
            get { return _Scorecard.UpperSectionTotal; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ThreeOfAKind
        {
            get { return _Scorecard.ThreeOfAKind; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int FourOfAKind
        {
            get { return _Scorecard.FourOfAKind; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int FullHouse
        {
            get { return _Scorecard.FullHouse; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int SmallStraight
        {
            get { return _Scorecard.SmallStraight; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int LargeStraight
        {
            get { return _Scorecard.LargeStraight; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Yacht
        {
            get { return _Scorecard.Yacht; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Chance
        {
            get { return _Scorecard.Chance; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int YachtBonuses
        {
            get { return _Scorecard.YachtBonuses; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int LowerSectionTotal
        {
            get { return _Scorecard.LowerSectionTotal; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Total
        {
            get { return _Scorecard.Total; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public User User
        {
            get { return _Scorecard.User; }
            set { _Scorecard.User = value; }
        }

        public int GetScoreField(ScoreField scoreField)
        {
            return _Scorecard.GetScoreField(scoreField);
        }

        public bool IsGameOver()
        {
            return _Scorecard.IsGameOver();
        }

        public bool IsScoreFieldSet(ScoreField scoreField)
        {
            return _Scorecard.IsScoreFieldSet(scoreField);
        }

        public void Reset()
        {
            _Scorecard.Reset();
            UpdateDisplay();
        }

        public bool SetScoreField(ScoreField scoreField, Dice dice)
        {
            return _Scorecard.SetScoreField(scoreField, dice);
        }
        #endregion
    }
}
